-- 阶跃算子:
--   将矩阵元素值的绝对值舍入至就近的 2 的整数次幂，即数值落在 2 的等比数列内，{..., 0.125, 0.25, 0.5, 1.0, 2.0, 4.0, 8.0, 16.0, ...}。0 值保持不变为 0。
--   算法: 用对数运算、四舍五入为整数、再幂运算

-- 阶跃矩阵乘法的 IC 设计优化
--   浮点乘法运算指令，可用单次加法指令代替。
--     x * 2, 即，浮点 x 的指数位 + 1；
--     x * 8, 即，浮点 x 的指数位 + 3；
--     x / 2, 即，浮点 x 的指数位 - 1；
--     x / 32, 即，浮点 x 的指数位 - 5；
--   或可用单次位移指令替代。位移位数即第二目的幂指数。
--     x * 2, 即，浮点 x 的尾数位左移 1 位；
--     x * 8, 即，浮点 x 的尾数位左移 3 位；
--     x / 2, 即，浮点 x 的尾数位右移 1 位；
--     x / 32, 即，浮点 x 的尾数位右移 5 位；

-- 阶跃算法：
--   当 a = 0 时，a` = 0；
--   当 a <> 0 时，a` = sign(a) * (2 ^ round(abs(a)))

-- 在实际执行计算时，不需要执行复杂的符号、乘法、幂、四舍五入或绝对值等单步算数运算，
-- 而只需要判断 float 的尾数位是否大于等于 2 ^ 0.5；如果是，则指数位加一；如果不是，指数不变；然后符号位不变、尾数位设置为 1。

-- 矩阵乘法阶跃算法:
--   对于矩阵乘法 C = A * B, 其阶跃算法 C` = A * B`

-- 矩阵乘法阶跃算法第二目压缩存取:
--   B_pow = log2(B), C` = A * B` = A * (2 ^ B_pow)
--   B_pow 需要更小的数位即可存取传输。
--      比如 int6 的 B_pow, 即可支持 FP32 的 B ∈ [-(2 ^ (2 ^ 5)), 2 ^ (2 ^ 5)], 最小精度 2 ^ (-(2 ^ 5))。


-- drop function if exists sm_sc.fv_arr_to_power2(float[]);
create or replace function sm_sc.fv_arr_to_power2
(
  i_arr      float[]
)
returns float[]
as
$$
  import numpy as np
  v_arr = np.float64(i_arr)
  # 0 值特殊预处理
  v_arr = np.where(v_arr == 0, np.nan, v_arr)
  # 用对数运算、四舍五入为整数、再幂运算的方法，将元素绝对值舍入就近 2 的整数次幂值
  v_arr = (2.0 ** np.round(np.log2(np.abs(v_arr)))) * np.sign(v_arr)
  # 0 值特殊后处理
  v_arr = np.nan_to_num(v_arr, nan = 0.0)
  return v_arr.tolist()
$$
language plpython3u stable
parallel safe
;

-- with
-- cte_arr as 
-- (
--   select 
--     sm_sc.fv_new_randn(0.0, 0.25, array[3, 4]) *` 16.0 as a_arr
-- )
-- select 
--   a_arr :: decimal[] ~=` 3
-- , sm_sc.fv_arr_to_power2(a_arr)
-- from cte_arr

-- select sm_sc.fv_arr_to_power2(array[0.0])